; DATE 10/07/83  09:43   last revision
;
; SIG/M release REVIDEO set TRUE & INTOREV & OUTAREV set for ADM-31
;
	TITLE	'SD Version 4.8B for CP/M-86'
;
; DATE 10/03/83  14:32
; by: H.M. Van Tassell 120 Hill Hollow Rd, Watchung, NJ 07060 (201)755-5372
;
; This is an XLT86 translation of SD-4.8B the original of which was released
; on SIG/M vol 145, see the original release for history and author credits.
;
; The "usual" minor changes have been made to enable the translation. 
; References to CP/M ver 2.2 and lower have been deleted, segment override
; added where required, and the version notice updated.
;
; NOTE: XLT86 deletes all conditional statements so this version is setup
; as shown in the USER OPTION SPECIFICATIONS. None of these options will
; work and are included here as reference for those who wish to add back
; any of the OPTIONS.
; I did add the REVIDEO option for those who dont wish to use enhanced video.
;
; This version should work with CP/M-86 ver 1.1, Concurrent 3.0, and Plus 3.1
; It works with my beta test copy of PLUS 3.1 but I have not tested Concurrent.
;
;		 To generate program:
;		 ASM86 SD $SZPZ
;		 GENCMD SD 8080 CODE[M3FF]
;
;
;>>>> The following notes appeared in the preamble to CP/M-80 SD-4.8B. <<<<
;
; Made several changes to make SD more useful and friendly to me and maybe
; to others. Added HELP if // is filename or if a bad option specified.
; Changed so will always start with drive A: if D option choosen and no
; drive is specified in command line, user = 0 if A option choosen and no
; user was specified as an option,-see the new STARTZ, ZUSR & ZDRV.
; Added STSKIP & SKIPTO to skip over drives that dont exist but are in
; drive table. Useful for some of us that use M: to specify memory drive.
; To more closely conform to Digial Research notation used in CP/M PLUS,
; changed from "$" to "[]" to indicate options and eliminated need for a
; leading space and allow the use of either space or comma between options.
; Added code for 3 byte INTO/OUTA reverse video so can use esc,G,x code to
; indicate file attributes. Since directorys can be long, added a message 
; at start of each section, see STMSG equate. Added option 'T' which is
; a sort by filetype and then by filename, TOPREV reverses 'T' option.
; NOTE: Edit help message at start of code to match your choice of options
;			 SD.ASM Ver 4.8A
;			  (Rev 14 MAY 83)
;
;		    SUPER DIRECTORY PROGRAM
;		       by Bruce	R. Ratoff
;
;	    Based on 'DIRS' by Keith Petersen, W8SDZ
;
; Displays the directory of a CP/M disk, sorted	alphabetically,
; with the file	size in	K, rounded to the nearest CP/M block size.
;
; This latest variation	on a common theme will automatically adjust
; itself for any block size and	directory length under CP/M 1.4	or 2.x
; or MP/M (any version).  Provisions are made for (1) automatic	pauses
; when the screen fills	up; (2)	searching individual or	multiple drives
; and/or user areas; (3) unconditional or optionally resetting the disk
; system before	execution begins; (4) directing	output to a disk file
; and appending	to it on subsequent runs; (5) summary line output giving
; drive	and user information, #	of files matched and how much space they
; consume, and the amount of free space	remaining on the disk; (6) displaying
; or suppressing CP/M 2	"system" files;	(7) accepting ambiguous	filenames
; with or without a drive name;	and (8)	printer	output.
; See SD.DOC for detailed instructions on configuring and running SD.ASM
;
		;******************************
		;*			      *
		;* USER OPTION SPECIFICATIONS *
		;*			      *
		;******************************
;
;****** NOTE: none of these work - they are here for reference only *****
;
;ALTCPM	EQU	FALSE		;True for H8 or	TRS-80
;DIRCON	EQU	FALSE		;True for direct console output
;LOWCCP	EQU	FALSE		;True only if you are running with BYELOW
;BDOSPG	EQU	0CCh		;Set properly if LOWCCP is true
;OPTION	EQU	TRUE		;True if allowing ANY command line options
;AOPT	EQU	TRUE		;True to allow searching all user areas
;DOPT	EQU	TRUE		;True to allow searching all drives on-line
;STARTZ	EQU	TRUE		;True to start with zero dr & user in none spec
;FOPT	EQU	TRUE		;True to allow file output option
;NOPT	EQU	TRUE		;True to allow disabling page pause option
;PGPAWZ	EQU	TRUE		;True for pause after each page
;POPT	EQU	TRUE		;True to allow printer option
;REPERR	EQU	TRUE		;True to report command line option errors
;REPSIZ	EQU	TRUE		;True to report file sizes
;REPUSR	EQU	TRUE		;True to report user numbers
;RMAC	EQU	FALSE		;True for RMAC assembly and external def link
;ROPT	EQU	TRUE		;True to allow reset option
;SOPT	EQU	TRUE		;True to allow system file option
;TOPT	EQU	TRUE		;True for sort by filetype & filename
;TOPREV	EQU	FALSE		;False to reverse "T" opt for normal sort
;UOPT	EQU	TRUE		;True to allow user number option
;VOPT	EQU	TRUE		;True to allow version number display
;LOPT	EQU	TRUE		;True to allow Library file member printing
;LOPREV	EQU	FALSE		;False to reverse the "L" option and not print
;************ See above NOTE in reference to above equates **************

;===========================================================================
M	EQU	Byte Ptr 0[BX]
FALSE	EQU	0
TRUE	EQU	NOT FALSE

REVIDEO	EQU	TRUE		;Use reverse video sequences
LEADIN	EQU	27		;Esc. leadin for ADM-31 & Z19
; INTOREV EQU	3847H		;Seq. to enter underline mode (for WYSE-200)
; OUTAREV EQU	3047H		;and to get out ((note digits are reversed))
; NOTE: use a 2 digit INTO/OUTA sequence &  set one to 00 if only one needed
INTOREV	EQU	0029H		;Dim video for ADM-31 type terminals
OUTAREV	EQU	0028H		; "    "    "   "      "     "
; INTOREV	EQU	0070H		;Sequence to enter reverse video (Z19)
; OUTAREV	EQU	0071H		;And to get out
; LEADIN	EQU	'~'		;Tilda leadin for hazeltine 1500
; INTOREV	EQU	0019H		;Background mode on Hazeltine 1500
; OUTAREV	EQU	001FH		;Foreground mode on Hazeltine 1500
DELIM	EQU	7CH		;Fence (delimiter) character (vertical bar)
;DELIM	EQU	20H		;Space
NPL	EQU	4		;# of names per line (max of 3 for 64x16)
;						     (max of 4 for 80x24)
LPS	EQU	23		;# of lines per screen (max of 14 for 64x16)
;						       (max of 23 for 80x24)
STMSG	EQU	TRUE		;true to start each directory with message
;
STSKIP	EQU	3		;Start skipping at this drive number
SKIPTO	EQU	13		;and skip to this drive number
;
; BDOS equates
;
RDCHR	EQU	1		;Read char from	console
WRCHR	EQU	2		;Write char to console
CONST	EQU	11		;Check cons stat
RESET	EQU	13		;Reset disk system
SELDSK	EQU	14		;Select	disk
OPEN	EQU	15		;0FFH=not found
CLOSE	EQU	16		;   "	"
SEARCH	EQU	17		;   "	"
NEXT	EQU	18		;   "	"
READ	EQU	20		;not 0 = EOF
WRITE	EQU	21		;not 0 = disk full
MAKE	EQU	22		;0FFH =	directory full
CURDSK	EQU	25		;Get currently logged disk name
SETDMA	EQU	26		;Set current DMA
GALLOC	EQU	27		;Get address of	allocation vector
CURDPB	EQU	31		;Get current disk parameters
CURUSR	EQU	32		;Get currently logged user number 

BASE	EQU	0		;Default to 0 (or 100H with MAC	+R option)
TPA	EQU	100H
FCB	EQU	BASE+5CH
BDOS	EQU	BASE+5
	
	ORG	TPA
;********************************
;				*
; BEGIN	EXECUTABLE PROGRAM CODE	*
;				*
;********************************
	JMP	START
L_1	EQU	$
	DSEG
	ORG	Offset L_1
MLEADIN	DB	LEADIN		;leadin char for hilite sequences
MINTOREV DW	INTOREV		;code to enter hilite mode
MOUTAREV DW	OUTAREV		;code to return to normal mode
MDELIM	DB	DELIM		;char to delimit columns on display
MNPL	DB	NPL		;names per line (# of columns)
MLPS	DB	LPS		;lines per screen (between pauses)

VERNAME	RS	0
	DB	'SD 4.8B-86  03Oct83/HMVT          ',0

HLPMES	DB	13,10,10
	DB	'Useage: SD dr:FILENAME.TYP [option,option...]'
	DB	13,10,10,'Drive and/or filespec are optional'
	DB	13,10,'Wildcards * and ? allowed'
	DB	13,10,'SD // for help'
	DB	13,10,10,'Options Are:'
	DB	13,10,09,'A=All Users'
	DB	13,10,09,'D=All Disks'
	DB	13,10,09,'F=Make File'
	DB	13,10,09,'L=Library'
	DB	13,10,09,'N=No Paging'
	DB	13,10,09,'P=Printer'
	DB	13,10,09,'R=Reset'
	DB	13,10,09,'S=System'
	DB	13,10,09,'T=Sort by Type'
	DB	13,10,09,'Un=User No.'
	DB	13,10,09,'V=Version',0
L_2	EQU	$
	CSEG
	ORG	Offset L_2
START:
	MOV	CL,12			;Get and save the CP/M version #
	INT	224
	MOV	AL,BL
	MOV	Byte Ptr VERFLG,AL
	MOV	DL,0FFH			;Get current user number
	MOV	CL,CURUSR		
	CALL	CPM
	MOV	Byte Ptr OLDUSR,AL	;Initialize startup user number
	MOV	Byte Ptr NEWUSR,AL	;..and make new	user match it
	MOV	Byte Ptr BASUSR,AL	;Save extra copy for multi-disk	directories
	MOV	CL,CURDSK
	CALL	CPM			;Get current disk nr
	MOV	Byte Ptr OLDDSK,AL	;Save for reset	if needed
	INC	AL
	MOV	Byte Ptr OUTFCB,AL	;Set directory output file drive
; If at	least one option is allowed, scan the command line for the
; option field delimiter.  The option field delimiter is considered
; valid	only if	it is preceded by at least 1 space (otherwise, it
; may be part of the directory filename).  Any unrecognized options
; or illegal user numbers will be flagged or ignored (see REPERR).
; (We scan the command line buffer rather than the 2nd default FCB
; because all 8	options	plus a 2 digit user number won't fit in
; the FCB name field).
	MOV	BX,80H			;Set command line buffer+2 pointer
	MOV	CH,M			;Get length of command line buffer
; Check for a help request
	INC	BX
	INC	BX
	MOV	AL,M
	CMP	AL,'/'
	JNZ	L_4	
	CALL	HELP
L_4:
	LAHF
	DEC	BX
	SAHF
	LAHF
	DEC	BX
	SAHF
; Search for the command line delimiter.  If not found, assume no options.
SCNDOL:	LAHF
	INC	BX
	SAHF
	DEC	CH
	JNS	L_5	
	JMP	CKREST			;Exit if command line buffer empty
L_5:
	MOV	AL,M
	CMP	AL,'['			;Digital Research changing to "["
	JNZ	SCNDOL
;	DCX	H		;'$' found - make sure space precedes it
;	MOV	A,M
;	INX	H
;	CPI	' '
;	JNZ	SCNDOL		;No space - ignore "$" and search again
; Valid	delimiter found.  Scan the rest	of the buffer for options.  Errors
; past this point will cause an	abort if the command line error	option is
; enabled.  Otherwise, the dud option will be ignored and SD will attempt
; to continue stumbling	through	the rest of the	field.
	XCHG	BX,DX			;Get option field pointer to DE
SCNOPT:	LAHF				;Bump to next option field character
	INC	DX
	SAHF
	DEC	CH			;Dock characters left in option	field
	JNS	L_6	
	JMP	CKREST			;If option field exhausted, exit
L_6:
SCNAGN:	MOV	SI,DX			;Get the next option character
	MOV	AL,[SI]
	CMP	AL,' '			;Do we have a space?
	JZ	SCNOPT			;Ignore	it if so
	CMP	AL,','			;Same for comma
	JZ	SCNOPT
	CMP	AL,']'			;End of option list
	JNZ	L_7	
	JMP	CKREST
L_7:
	MOV	BX,(Offset OTBL)-1	;Get base of option lookup table
	MOV	CL,(Offset OEND)-(Offset OTBL)+1	;Get length of option lookup table
NOMACH:	INC	BX			;Bump to next option table character
	DEC	CL			;Are we	out of the table?
	JZ	CK4USR			;If so, check for user option
	CMP	AL,M			;Compare our character with option table
	JNZ	NOMACH			;Exit if no match
	MOV	M,0			;Otherwise, activate the flag
	JMPS	SCNOPT			;..and go get the next option character
; If option character doesn't match the table, see if we have a User
; option.
CK4USR:				;Check for user	number option
	CMP	AL,'U'
	JNZ	CLERR			;Last option, so bad deal if that ain't it
UAGN:	LAHF				;Bump to user number digit
	INC	DX
	SAHF
	DEC	CH
	JS	CLERR			;Error if nothing left
	MOV	SI,DX			;Get decimal digit
	MOV	AL,[SI]
	CMP	AL,' '			;Ignore	leading	spaces
	JZ	UAGN
	SUB	AL,30H			;Subtract ASCII	BIAS
	JB	CLERR			;Error if < 0
	CMP	AL,10
	JNB	CLERR			;Error if > 9
	MOV	Byte Ptr NEWUSR,AL	;Save user number as it	may be only 1 digit
	MOV	Byte Ptr BASUSR,AL	;Duplicate it if multi-disk mode
	LAHF
	XCHG	AL,AH
	PUSH	AX
	MOV	AL,0FFH			;Set got user flag
	MOV	Byte Ptr GOTUSR,AL
	POP	AX
	XCHG	AL,AH
	SAHF
	LAHF				;Bump to possible 2nd digit of user number
	INC	DX
	SAHF
	DEC	CH
	JS	CKREST			;If no more buffer, exit with complete user #
	MOV	SI,DX			;Else, check for another digit
	MOV	AL,[SI]
	SUB	AL,30H
	JNB	L_8	
	JMP	SCNAGN			;If next char not numeric, its not part	of
L_8:
	CMP	AL,10			;..user	number so go check for another option
	JNAE	L_9	
	JMP	SCNAGN
L_9:
	MOV	BL,AL			;Save units digit
	MOV	AL,Byte Ptr NEWUSR	;Get tens digit
	ADD	AL,AL			;Multiply by 10
	MOV	BH,AL
	ADD	AL,AL
	ADD	AL,AL
	ADD	AL,BH
	ADD	AL,BL			;Combine with units digit
	MOV	Byte Ptr NEWUSR,AL	;Save the total	user number
	MOV	Byte Ptr BASUSR,AL	;Duplicate it if multi-disk mode
	JMP	SCNOPT			;Continue scanning
;Balance UOPT
; If command line error	option enabled,	playback the command line up
; to the character that	we gagged on and exit.	If REPERR is not enabled,
; then continue	as if nothing were amiss to avoid acknowledging	that
; some options are available.
CLERR:
	XOR	AL,AL
	LAHF				;Tag end of command line with terminator
	INC	DX
	SAHF
	MOV	SI,DX
	MOV	[SI],AL
	CALL	CRLF
	MOV	DX,(Offset ERRMS2)
	CALL	PRINT
	MOV	DX,(Offset ERRTAG)
	CALL	PRINT
	MOV	BX,81H			;Playback bad command line to error point
CLELP:	MOV	AL,M
	OR	AL,AL
	JZ	CLEX
	CALL	TTYPE
	INC	BX
	JMPS	CLELP
CLEX:	MOV	AL,'?'			;Tag line with a '?' field
	CALL	TTYPE
	JMP	HELP1			;..and tell how to do it
;Balance OPTION
; Options input or not specified.  If reset option specified, reset
; the disk system now.	Its important that the reset be done OUTSIDE
; the multiple drive loop if the file output option is enabled because
; CP/M 1.4 clobbers the DMA buffer on reset.
CKREST:
	MOV	AL,Byte Ptr ROPFLG	;If reset flag set, reset disk system before
	OR	AL,AL			;..starting to update allocation vectors
	MOV	CL,RESET
	JNZ	L_10	
	CALL	CPM
L_10:
;Balance ROPT
	MOV	AL,Byte Ptr VOPFLG	;If version display flag set, print it
	OR	AL,AL
	JNZ	NOVOPT
	MOV	DX,(Offset VERNAME)
	CALL	PRINT
	CALL	CRLF
NOVOPT:
	MOV	AL,Byte Ptr DOPFLG	;If multi-disk flag set, 
	OR	AL,AL			;..need to set error traps
	JNZ	L_11	
	CALL	SWAPEM			;Swap BDOS error vector	tables
L_11:
; If "A" option choosen and no user number specified
; set user to zero
ZUSR:	MOV	AL,Byte Ptr GOTUSR	;Was a user specified?
	OR	AL,AL
	JNZ	ZDRV			;YES-quit now
	MOV	AL,Byte Ptr AOPFLG	;Was D option specified?
	OR	AL,AL
	JNZ	ZDRV			;JMP if NO
	XOR	AL,AL
	MOV	Byte Ptr NEWUSR,AL	;ZERO NEWUSR for all users
	MOV	Byte Ptr BASUSR,AL	;ZERO BASUSR for all users
; If "D" option choosen and no drive specified
; set drive to physical zero
ZDRV:	MOV	BX,FCB
	MOV	AL,M			;Get drive name	for directory search
	OR	AL,AL			;Any specified?
	JNZ	NOOPT			;Yes skip next routine
	MOV	AL,Byte Ptr DOPFLG	;else if DOPT then
	OR	AL,AL
	JNZ	ZDRV1
	INC	AL			;always start with A
	MOV	M,AL
	JMPS	NOOPT
ZDRV1:	MOV	AL,Byte Ptr OLDDSK	;Otherwise, get	default	disk
	INC	AL
	MOV	M,AL			;Put the absolute drive	code in	directory FCB
; Validate drive code and user area numbers from the drive table.
NOOPT:	MOV	DX,(Offset DREMSG)	;Get the drive/user error message
	PUSH	DX
	MOV	AL,Byte Ptr .FCB	;Get directory drive code
	DEC	AL			;Normalize to range of 0-15
	CMP	AL,(Offset HIDRV)-(Offset LODRV)	;Compare with maximum drives on-line
	JNAE	L_12	
	JMP	ERXIT			;Take drive error exit if out of range
L_12:
	MOV	BX,(Offset USRMSG)	;Switch	to user	# error	message
	MOV	BP,SP
	XCHG	BX,[BP]
	MOV	DL,AL			;Use drive code	as index into table
	MOV	DH,0
	MOV	BX,(Offset LODRV)	;Point to base of drive/user table
	ADD	BX,DX
	MOV	AL,M			;Get the maximum user #	for this drive
	AND	AL,0FH			;Make sure its in range	0 - 15
	MOV	Byte Ptr MAXUSR,AL	;Save it for later
	MOV	BX,(Offset NEWUSR)	;Point to the directory	user area
	CMP	AL,M			;Compare it with the maximum
	JNB	L_13	
	JMP	ERXIT			;Take error exit if user number	illegal
L_13:
	POP	DX			;Destroy error message pointer
	MOV	BX,FCB+1		;Point to name
	MOV	AL,M			;Any specified?
	CMP	AL,'['
	JZ	GOTDL
	CMP	AL,' '
	JNZ	GOTFCB
; No FCB - make	FCB all	'?'
GOTDL:	MOV	CH,11			;FN+FT count
QLOOP:	MOV	M,'?'			;Store '?' IN FCB
	INC	BX
	DEC	CH
	JNZ	QLOOP
GOTFCB:	MOV	AL,'?'			;Force wild extent
	MOV	Byte Ptr .FCB+12,AL
	CALL	SETSRC			;Set DMA for BDOS media change check
	MOV	BX,FCB			;Point to FCB drive code for directory
	MOV	DL,M			;Get the drive code out of the FCB
	DEC	DL			;Normalize drive code for SELECT
	MOV	CL,SELDSK		;Select the directory drive to retrieve
	CALL	CPM			;..the proper allocation vector
	MOV	CL,CURDPB		;request DPB
	INT	224
	INC	BX
	INC	BX
	MOV	AL,ES:M			;Get block shift
	MOV	Byte Ptr BLKSHF,AL
	INC	BX			;Bump to block mask
	MOV	AL,ES:M
	MOV	Byte Ptr BLKMSK,AL	;Get it
	INC	BX
	INC	BX
	MOV	DL,ES:M			;Get max block #
	INC	BX
	MOV	DH,ES:M
	XCHG	BX,DX
	MOV	Word Ptr BLKMAX,BX	;Save it
	XCHG	BX,DX
	INC	BX
	MOV	DL,ES:M			;Get directory size
	INC	BX
	MOV	DH,ES:M
	XCHG	BX,DX

; Calculate # of K free	on selected drive now so that the FREE figure
; will not reflect either the creation or additions to the SD.DIR
; file (which we would probably	erase or move anyway).
FREE:	MOV	Word Ptr DIRMAX,BX	;Save max # of entries in directory
	MOV	AL,Byte Ptr VERFLG	;check version #
	CMP	AL,30H			;3.0 or higher?
	JB	FREE20			;use old method if not
	MOV	AL,Byte Ptr .FCB	;get drive #
	DEC	AL
	MOV	DL,AL			;Use new Compute Free Space BDOS call
	MOV	CL,46
	CALL	CPM
	MOV	CL,3			;answer is a 24-bit integer
FRE3L1:
	MOV	BX,BASE+82H		;answer is in 1st 3 bytes of DMA adr
	MOV	CH,3			;convert it from sectors to K
	OR	AL,AL			;by dividing by 8
FRE3L2:
	MOV	AL,M
	RCR	AL,1
	MOV	M,AL
	DEC	BX
	DEC	CH
	JNZ	FRE3L2			;Loop for 3 bytes
	DEC	CL
	JNZ	FRE3L1			;Shift 3 times
	MOV	BX,Word Ptr .BASE+80H	;Now get result in K
	JMPS	SAVFRE			;Go store it
FREE20:
	MOV	CL,GALLOC		;Get address of	allocation vector
	INT	224			;address ret as ES:BX
	XCHG	BX,DX			
	MOV	BX,Word Ptr BLKMAX	;Get its length
	INC	BX
	MOV	CX,0			;Init block count to 0
GSPBYT:	PUSH	DX			;Save alloc address
	MOV	SI,DX			;offset in SI
	MOV	AL,ES:[SI]		;seg in ES
	MOV	DL,8			;Set to	process	8 blocks
GSPLUP:	RCL	AL,1			;Test bit
	JB	NOTFRE
	INC	CX
NOTFRE:	MOV	DH,AL			;Save bits
	DEC	BX			;Count down blocks
	MOV	AL,BL
	OR	AL,BH
	JZ	ENDALC			;Quit if out of	blocks
	MOV	AL,DH			;Restore bits
	DEC	DL			;Count down 8 bits
	JNZ	GSPLUP			;Do another bit
	POP	DX			;Bump to next byte..
	INC	DX			;..of alloc. vector
	JMPS	GSPBYT			;Process it
ENDALC:	POP	DX			;Clear allocation vector pointer from stack
	MOV	BL,CL			;Copy blocks to	hl
	MOV	BH,CH
	MOV	AL,Byte Ptr BLKSHF	;Get block shift factor
	SUB	AL,3			;Convert from sectors to K
	JZ	SAVFRE			;Skip shifts if 1K blocks - return free in HL
FREKLP:	SHL	BX,1			;Multiply blocks by K/BLK
	DEC	AL
	JNZ	FREKLP
SAVFRE:	MOV	Word Ptr FREEBY,BX	;Save the free space for output later
; Reenter here on subsequent passes while in the all-users mode
SETTBL:	MOV	BX,Word Ptr DIRMAX	;Get directory maximum again
	INC	BX			;Directory size	is DIRMAX+1
	SHL	BX,1			;Double	directory size
	MOV	DX,(Offset ORDER)	;To get	size of	order table
	ADD	BX,DX			;Allocate order	table
	MOV	Word Ptr TBLOC,BX	;Name table begins where order table ends
	MOV	Word Ptr NEXTT,BX
	XCHG	BX,DX
	MOV	BX,Word Ptr .BDOS+1	;Make sure we have room	to continue
	MOV	AL,DL
	SUB	AL,BL
	MOV	AL,DH
	SBB	AL,BH
	JNAE	L_14	
	JMP	OUTMEM
L_14:
	MOV	AL,Byte Ptr NEWUSR	;Get user area for directory
	MOV	DL,AL
	MOV	CL,CURUSR		;Get the user function
	CALL	CPM			;..and set new user number

; Look up the FCB in the directory
SFIRST:	MOV	BX,0
	MOV	Word Ptr COUNT,BX	;Initialize match counter
	MOV	Word Ptr TOTFIL,BX	;     "	    total file counter
	MOV	Word Ptr TOTSIZ,BX	;     "	    total size counter
	CALL	SETSRC			;Set DMA for directory search
	MOV	CL,SEARCH		;Get 'search first' function
	JMPS	LOOK			;..and go search for 1st match
; Read more directory entries
MORDIR:	MOV	CL,NEXT			;Search	next
LOOK:	MOV	DX,FCB
	CALL	CPM			;Read directory	entry
	INC	AL			;Check for end (0FFH)
	JNZ	L_16	
	JMP	SPRINT			;If no more, sort & print what we have
L_16:
; Point	to directory entry
SOME:	DEC	AL			;Undo prev 'INR A'
	AND	AL,3			;Make modulus 4
	ADD	AL,AL			;Multiply...
	ADD	AL,AL			;..by 32 because
	ADD	AL,AL			;..each	directory
	ADD	AL,AL			;..entry is 32
	ADD	AL,AL			;..bytes long
	MOV	BX,BASE+81H		;Point to buffer
;(skip to FN/FT)
	ADD	AL,BL			;Point to entry
	ADD	AL,9			;Point to SYS byte
	MOV	BL,AL			;Save (can't carry to H)
	MOV	AL,Byte Ptr SOPFLG	;Did user request SYS files?
	OR	AL,AL
	JZ	SYSFOK
	MOV	AL,M			;Get SYS byte
	OR	AL,AL			;Check bit 7
	JS	MORDIR			;Skip that file
SYSFOK:	MOV	AL,BL			;Go back now
	SUB	AL,10			;Back to user number (alloc flag)
	MOV	BL,AL			;HL points to entry now
	MOV	AL,Byte Ptr NEWUSR	;Get current user
	CMP	AL,M
	JNZ	MORDIR			;Ignore	if different
	INC	BX
; Move entry to	table
	XCHG	BX,DX			;Entry to DE
	MOV	BX,Word Ptr NEXTT	;Next table entry to HL
	MOV	CH,12			;Entry length (name, type, extent)
TMOVE:	MOV	SI,DX			;Get entry char
	MOV	AL,[SI]
	MOV	M,AL			;Store in table
	INC	DX
	INC	BX
	DEC	CH			;More?
	JNZ	TMOVE
	INC	DX
	INC	DX			;Point to sector count
	MOV	SI,DX			;Get it
	MOV	AL,[SI]
	MOV	M,AL			;Store in table
	INC	BX
	MOV	Word Ptr NEXTT,BX	;Save updated table addr
	XCHG	BX,DX
	MOV	BX,Word Ptr COUNT	;Bump the # of matches made
	INC	BX
	MOV	Word Ptr COUNT,BX
	MOV	BX,13			;Size of next entry
	ADD	BX,DX
	XCHG	BX,DX			;Future	NEXTT is in DE
	MOV	BX,Word Ptr .BDOS+1	;Pick up TPA end
	MOV	AL,DL
	SUB	AL,BL			;Compare NEXTT-TPA end
	MOV	AL,DH
	SBB	AL,BH
	JNB	L_17	
	JMP	MORDIR			;If TPA	END > NEXTT then loop back for more
L_17:
OUTMEM:	CALL	ERXIT			;Exit if directory too large
	DB	'Memor','y',0
; Sort and print
SPRINT:
	CALL	SETFOP			;Return to file output DMA & user #
	MOV	BX,Word Ptr COUNT	;Get file name count
	MOV	AL,BL
	OR	AL,BH			;Any found?
	JNZ	L_18	
	JMP	PRTOTL			;Exit if no files found
L_18:
	PUSH	BX			;Save file count
	MOV	Byte Ptr SUPSPC,AL	;Enable	leading	zero suppression
; Initialize the order table
	MOV	BX,Word Ptr TBLOC	;Get start of name table
	XCHG	BX,DX			;Into DE
	MOV	BX,(Offset ORDER)	;Point to order	table
	MOV	CX,13			;Entry length
BLDORD:	MOV	M,DL			;Save low order	address
	INC	BX
	MOV	M,DH			;Save high order address
	INC	BX
	XCHG	BX,DX			;Table addr to HL
	ADD	BX,CX			;Point to next entry
	XCHG	BX,DX
	MOV	BP,SP			;Save tbl addr,	fetch loop counter
	XCHG	BX,[BP]
	DEC	BX			;Count down loop
	MOV	AL,BL
	OR	AL,BH			;More?
	MOV	BP,SP			;(restore tbl addr, save counter)
	XCHG	BX,[BP]
	JNZ	BLDORD			;Yes, go do another one
	POP	BX			;Clean loop counter off	stack
	MOV	BX,Word Ptr COUNT	;Get count
	MOV	Word Ptr SCOUNT,BX	;Save as # to sort
	DEC	BX			;Only 1	entry?
	MOV	AL,BL
	OR	AL,BH
	JNZ	L_19	
	JMP	DONE			;Yes, so skip sort
L_19:
; This sort routine is adapted from SOFTWARE TOOLS
; by Kernigan and Plaugher.
SORT:	MOV	BX,Word Ptr SCOUNT	;Number	of entries
L0:	OR	AL,AL			;Clear carry
	MOV	AL,BH			;GAP=GAP/2
	RCR	AL,1
	MOV	BH,AL
	MOV	AL,BL
	RCR	AL,1
	MOV	BL,AL
	OR	AL,BH			;Is it zero?
	JNZ	L_20	
	JMP	DONE			;Then none left
L_20:
	MOV	AL,BL			;Make gap odd
	OR	AL,01
	MOV	BL,AL
	MOV	Word Ptr GAP,BX
	INC	BX			;I=GAP+1
SL2:	MOV	Word Ptr I,BX
	XCHG	BX,DX
	MOV	BX,Word Ptr GAP
	MOV	AL,DL			;J=I-GAP
	SUB	AL,BL
	MOV	BL,AL
	MOV	AL,DH
	SBB	AL,BH
	MOV	BH,AL
SL3:	MOV	Word Ptr J,BX
	XCHG	BX,DX
	MOV	BX,Word Ptr GAP		;JIG=J+GAP
	LAHF
	ADD	BX,DX
	SAHF
	MOV	Word Ptr JIG,BX
	CALL	COMPARE			;Compare (J) and (JIG)
	JNS	SL5			;If A(J)<=A(JIG)
	MOV	BX,Word Ptr J
	XCHG	BX,DX
	MOV	BX,Word Ptr JIG
	CALL	SWAP			;Exchange A(J) and A(JIG)
	MOV	BX,Word Ptr J		;J=J-GAP
	XCHG	BX,DX
	MOV	BX,Word Ptr GAP
	MOV	AL,DL
	SUB	AL,BL
	MOV	BL,AL
	MOV	AL,DH
	SBB	AL,BH
	MOV	BH,AL
	JS	SL5			;If J>0	GOTO SL3
	OR	AL,BL			;Check for zero
	JZ	SL5
	JMPS	SL3
SL5:	MOV	BX,Word Ptr SCOUNT	;For later
	XCHG	BX,DX
	MOV	BX,Word Ptr I		;I=I+1
	INC	BX
	MOV	AL,DL			;IF I<=N GOTO SL2
	SUB	AL,BL
	MOV	AL,DH
	SBB	AL,BH
	JS	L_21	
	JMP	SL2
L_21:
	MOV	BX,Word Ptr GAP
	JMP	L0
; Sort is all done - print entries
DONE:					;If output option wanted, prepare file
	MOV	AL,Byte Ptr FOPFLG
	OR	AL,AL
	JZ	L_22	
	JMP	NOOUT			;If file output, fall through with A=0
L_22:
; If all user option enabled, and we're not on the first pass, then the
; output file is already open and positioned, so we can skip the open.
;	IF	AOPT
	MOV	BX,(Offset OPNFLG)	;point to output file open flag
	CMP	AL,M			;a=0, set z if opnflg=0 also
	JZ	L_23	
	JMP	NOOUT			;if opnflg not zero, skip open
L_23:
	DEC	M			;else, make opnflg not zero and open
;	ENDIF			;Balance AOPT
; First pass on file append - prepare SD.DIR to receive new or appended
; output.
	MOV	DX,(Offset OUTFCB)	;Does output file already exist?
	MOV	CL,SEARCH
	CALL	CPM
	INC	AL
	JNZ	OPENIT			;If it does, then open it for processing
	MOV	CL,MAKE			;Otherwise, create the output file
	CALL	CPM
	INC	AL
	JZ	L_24	
	JMP	NOOUT			;Continue if open successful
L_24:
; If make or open fails, declare error
OPNERR:	CALL	ERXIT
	DB	'Ope','n',0
WRTERR:	CALL	ERXIT
	DB	'Writ','e',0
; Output file already exists - open it and position to the last
; record of the	last extent.
OPENIT:	MOV	CL,OPEN			;Open 1st extent of output file
	CALL	CPM
	INC	AL
	JZ	OPNERR			;Bad deal if 1st won't open
OPNMOR:	MOV	AL,Byte Ptr OUTFCB+15
	CMP	AL,128
	JB	LSTEXT			;If RC<128, this is last extent
	MOV	BX,(Offset OUTFCB)+12
	INC	M			;Else, bump to next extent
	MOV	CL,OPEN			;..and try to open it
	CALL	CPM
	INC	AL
	JNZ	OPNMOR			;Continue opening extents til no more
	DEC	M			;Then, reopen preceding	extent
	MOV	CL,OPEN
	CALL	CPM
	MOV	AL,Byte Ptr OUTFCB+15	;Get RC	for the	last extent
; At this point, OUTFCB is opened to the last extent of the file,
; so read in the last record in the last extent.
LSTEXT:	OR	AL,AL			;Is this extent	empty?
	JZ	NOOUT			;If so, then we're starting a clean slate
	DEC	AL			;Normalize record count
	MOV	Byte Ptr OUTFCB+32,AL	;Set record number to read
	MOV	CL,READ			;..and read last record	of file
	CALL	CPM
	OR	AL,AL			;Was read successful?
	JZ	RDOK			;If so,	proceed	to scan	for EOF	mark
APERR:	CALL	ERXIT
	DB	'Appen','d',0
; We now have the last record in the file in our buffer.
; Scan the last	record for the EOF mark, indicating where
; we can start adding data.
RDOK:	MOV	BX,(Offset OUTBUF)	;Point to start of output buffer
	MOV	CH,128			;Get length of output buffer
SCAN:	MOV	AL,M
	CMP	AL,'Z'-40H		;Have we found end of file?
	JZ	RESCR			;If so, save pointers and reset CR
	LAHF
	INC	BX
	SAHF
	DEC	CH
	JNZ	SCAN			;Otherwise, keep looking til end of buffer
; If we find an explicit EOF mark in the last buffer (or an implied EOF
; if the last record is full), move the FCB record and extent pointers
; back to correct for the read operation so that our first write operation
; will effectively replace the last record of the SD.DIR file.
RESCR:	PUSH	BX			;Save EOF buffer pointer
	PUSH	CX			;Save EOF buffer remaining
	MOV	BX,(Offset OUTFCB)+32	;Get current record again
	DEC	M			;Dock it
	JNS	SAMEXT			;If CR >=0, we're still in same extent
	MOV	BX,(Offset OUTFCB)+12	;Else, move to previous	extent
	DEC	M
	MOV	CL,OPEN			;Then, reopen the previous extent
	CALL	CPM
	INC	AL
	JZ	APERR			;Append position error if we can't reopen
	MOV	AL,Byte Ptr OUTFCB+15	;Else, position to last record of extent
	DEC	AL
	MOV	Byte Ptr OUTFCB+32,AL
SAMEXT:	POP	AX			;Recall where EOF is in buffer
	XCHG	AL,AH
	SAHF
	MOV	Byte Ptr BUFCNT,AL	;..and set buffer counter
	POP	BX			;Recall next buffer pointer
	MOV	Word Ptr BUFPNT,BX	;.. and set pointer for first addition
;Balance FOPT
NOOUT:
	MOV	BX,Word Ptr COUNT
	MOV	Word Ptr LCOUNT,BX
	MOV	BX,0
	MOV	Word Ptr LBTOTL,BX
	MOV	Word Ptr LMTOTL,BX
	MOV	BX,(Offset ORDER)	;Initialize order table	pointer
	MOV	Word Ptr NEXTL,BX
	MOV	Word Ptr NEXTT,BX
;Print "Driectory for...
XXX:	MOV	DX,(Offset DIRMS1)	;at start of each section
	CALL	PRINT
	CALL	PRDIR			;Print " Drive, x User, y"
	MOV	AL,Byte Ptr LINCNT
	INC	AL			;Bump line count
	MOV	Byte Ptr LINCNT,AL
	JMP	NEWLIN			;Start new line	and output the files
; Output the directory files we've matched.
ENTRY:	MOV	BX,Word Ptr COUNT
	DEC	BX			;Dock file count
	MOV	Word Ptr COUNT,BX
	MOV	AL,BH			;Is this the last file?
	OR	AL,BL
	JZ	OKPRNT			;If COUNT=0, last file so skip compare
; Compare each entry to	make sure that it isn't part of a multiple
; extent file.	Go only	when we	have the last extent of	the file.
	PUSH	CX			;Save NPL
	CALL	CKABRT			;Check for abort code from keyboard
	MOV	BX,Word Ptr NEXTT
	MOV	AL,11
	CALL	COMPR			;Does this entry match next one?
	POP	CX			;Recall	NPL
	JNZ	OKPRNT			;No, print it
	INC	BX
	INC	BX			;Skip since highest extent comes last in list
	MOV	Word Ptr NEXTT,BX
	JMPS	ENTRY			;Loop back for next lowest extent
; Valid	entry obtained - spit it out.
OKPRNT:	MOV	BX,Word Ptr NEXTT	;Get order table pointer
	MOV	DL,M			;Get low order address
	INC	BX
	MOV	DH,M			;Get high order	address
	INC	BX
	MOV	Word Ptr NEXTT,BX	;Save updated table pointer
	XCHG	BX,DX			;Table entry to	HL
	MOV	CH,8			;File name length
	CALL	TYPEIT			;Type filename
	MOV	AL,'.'			;Period	after FN
	CALL	TTYPE
	MOV	CH,3			;Display 3 characters of filetype
	CALL	TYPEIT
; Compute the size of the file and update our summary datum.
	MOV	DL,M			;Get extent #
	MOV	DH,0
	INC	BX
	MOV	AL,M			;Get sector count of last extent
	XCHG	BX,DX
	SHL	BX,1			;# of extents times 16k
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	XCHG	BX,DX			;Save in DE
	MOV	BX,(Offset BLKMSK)
	ADD	AL,M			;Round last extent to block size
	ROR	AL,1
	ROR	AL,1			;Convert from sectors to K
	ROR	AL,1
	AND	AL,1FH
	MOV	BL,AL			;Add to	total K
	MOV	BH,0
	ADD	BX,DX
	MOV	AL,Byte Ptr BLKMSK	;Get SECTORS/BLK-1
	ROR	AL,1
	ROR	AL,1			;Convert to K/BLK
	ROR	AL,1
	AND	AL,1FH
	NOT	AL			;Use to	finish rounding
	AND	AL,BL
	MOV	BL,AL
	XCHG	BX,DX			;Save file size	in DE
	MOV	BX,Word Ptr TOTSIZ
	LAHF				;Add to	total used
	ADD	BX,DX
	RCR	SI,1
	SAHF
	RCL	SI,1
	MOV	Word Ptr TOTSIZ,BX
	MOV	BX,Word Ptr TOTFIL	;Increment file	count
	LAHF
	INC	BX
	SAHF
	MOV	Word Ptr TOTFIL,BX
	XCHG	BX,DX			;Get back file size
; If report size enabled, output the size of the individual file.
;If file size report wanted
	CALL	DECPRT			;..go print it
	MOV	AL,'k'			;..and follow with K size
	CALL	TTYPE
; One file output - test to see	if we have to output another one.
	MOV	BX,Word Ptr COUNT	;Get current file counter and test it
	MOV	AL,BH
	OR	AL,BL
	JNZ	L_25	
	JMP	PRTOTL			;If no more files, exit	to summary output
L_25:
; At least one more file to output - can we put	it on the current line?
	DEC	CL
	LAHF
	XCHG	AL,AH
	PUSH	AX
	JZ	L_26	
	CALL	FENCE			;If room left, output the fence	character
L_26:
	POP	AX
	XCHG	AL,AH
	SAHF
	JZ	L_27	
	JMP	ENTRY			;.. and	go output another file
L_27:
; Current line full, start a new one.
NEWLIN:	MOV	AL,Byte Ptr MNPL
	MOV	CL,AL			;Reset names per line counter
	CALL	CRLF			;Space down to next line
	MOV	AL,3			;If printing less than 4 wide
	CMP	AL,CL
	JB	NWLA
	MOV	AL,Byte Ptr .FCB	;.. precede new	line with drive	name
	ADD	AL,'A'-1
	CALL	TTYPE
;If reporting user numbers 
	CALL	TYPUSR

;Balance REPUSR
	MOV	AL,':'			;Tag header with a colon and a space
	CALL	FPAD			;..and exit back to ENTRY
NWLA:
	JMP	ENTRY			;Go back and output another file
; Print	HL in decimal with leading zero	suppression
DECPRT:	SUB	AL,AL			;Clear leading zero flag
	MOV	Byte Ptr LZFLG,AL
	MOV	DX,-1000		;Print 1000's digit
	CALL	DIGIT
	MOV	DX,-100			;Etc.
	CALL	DIGIT
	MOV	DX,-10
	CALL	DIGIT
	MOV	AL,'0'			;Get 1's digit
	ADD	AL,BL
	JMP	TTYPE
DIGIT:	MOV	CH,'0'			;Start off with	ASCII 0
DIGLP:	PUSH	BX			;Save current remainder
	ADD	BX,DX			;Subtract
	JNB	DIGEX			;Quit on overflow
	POP	AX			;Throw away remainder
	INC	CH			;Bump digit
	JMPS	DIGLP			;Loop back
DIGEX:	POP	BX			;Restore pointer
	MOV	AL,CH
	CMP	AL,'0'			;Zero digit?
	JNZ	DIGNZ			;No, type it
	MOV	AL,Byte Ptr LZFLG	;Leading zero?
	OR	AL,AL
	MOV	AL,'0'
	JZ	L_29	
	JMP	TTYPE			;Print digit
L_29:
	MOV	AL,Byte Ptr SUPSPC	;Get space suppression flag
	OR	AL,AL			;See if	printing file totals
	JNZ	L_30	
	RET				;Yes, don't give leading spaces
L_30:
	JMP	SPACE			;Leading zero...print space
DIGNZ:	MOV	Byte Ptr LZFLG,AL	;Set leading zero flag so next zero prints
	JMP	TTYPE			;And print digit
; Show total space and files used
PRTOTL:
	MOV	AL,Byte Ptr LOPFLG
	OR	AL,AL
	JNZ	L_31
	MOV	BX,Word Ptr TOTFIL	;How many files	did we match?
	MOV	AL,BH
	OR	AL,BL
	JZ	L_32	
	CALL	PRTLMEM			;Skip the Library check if we didn't find any
L_32:
L_31:
PRTOT1	EQU	(Offset $)
	XOR	AL,AL			;Get a zero to...
	MOV	Byte Ptr SUPSPC,AL	;Suppress leading spaces in totals
	MOV	BX,Word Ptr TOTFIL	;How many files	did we match?
	MOV	AL,BH
	OR	AL,BL
	JZ	NXTUSR			;Skip the summary if we	didn't find any
	PUSH	BX			;Save TOTFIL
	MOV	Byte Ptr FNDFLG,AL	;Set file found flag
	CALL	CRLF
	CALL	PRDIR			;Print "  Drive, x User, y"
NOUSER:	MOV	DX,(Offset TOTMS3)	;Print " CONTAINS "
	CALL	PRINT
	MOV	BX,Word Ptr TOTSIZ	;Print total K used by files matched
	CALL	DECPRT
	MOV	DX,(Offset TOTMS4)	;PRINT "K IN "
	CALL	PRINT
	POP	BX			;Recall	TOTFIL
	CALL	DECPRT			;Print number of files matched
	MOV	DX,(Offset TOTMS5)	;Print " FILES WITH "
	CALL	PRINT
	CALL	PRTFRE			;Output	free space remaining & " FREE."
; Directory for	one user area completed.  If all users option is
; selected, then go do another directory on the	next user number
; until	we exceed the maximum user # for the selected drive.
NXTUSR:				;If all	users option enabled
	MOV	AL,Byte Ptr AOPFLG	;If not	all users mode - skip next
	OR	AL,AL
	JNZ	GOCLZ
	CALL	CKABRT			;Check for user	abort first
	MOV	AL,Byte Ptr MAXUSR	;No abort - get	maximum	user number
	MOV	BX,(Offset NEWUSR)	;Bump directory	user number
	INC	M
	CMP	AL,M			;Does next user	# exceed maximum?
	JNAE	L_33	
	JMP	SETTBL			;Continue if more user areas to	go
L_33:
;AOPT
;If multi-disk option enabled
	MOV	AL,Byte Ptr BASUSR	;Reset base user number	for the
	MOV	M,AL			;..next	directory search
;Balance DOPT and AOPT
; We've finished all of our outputting.  Flush the remainder of the
; output buffer and close the file before going to exit routine.
GOCLZ:
;	LDA	FOPFLG		;Is file output mode active?
;	ORA	A
;	JNZ	NXTDSK		;If not, move to next drive, otherwise ..
;	STA	PASS1		;Make PASS1=0 to force reopen on next drive
	MOV	BX,(Offset OPNFLG)	;get file open status then reset flag to
	MOV	AL,M			;..force reopen on next pass
	MOV	M,0
	OR	AL,AL
	JZ	NXTDSK			;skip closing sd.dir if it wasn't opened
	MOV	BX,(Offset BUFCNT)
	MOV	AL,M			;RETRIEVE # OF UNFLUSHED CHARACTERS IN BUFFER
	MOV	M,128			;FORCE BUFCNT TO EMPTY STATUS FOR NEXT DRIVE
	OR	AL,AL			;IF BUFCNT=128, BUFFER EMPTY SO SET SIGN BIT
	JS	CLOZE			;close sd.dir if buffer is empty
	JZ	FLUSH			;write last record to sd.dir if buffer full
	MOV	BX,Word Ptr BUFPNT	;OTHERWISE, PAD UNUSED BUFFER WITH CTRL-ZS
PUTAGN:	MOV	M,'Z'-40H
	INC	BX
	DEC	AL
	JNZ	PUTAGN			;CONTINUE PADDING TIL BUFFER FILLED OUT
;	MVI	A,128
;	STA	BUFCNT		;Force buffer to empty status
FLUSH:	MOV	DX,(Offset OUTFCB)	;FLUSH THE LAST OUTPUT BUFFER
	MOV	CL,WRITE
	CALL	CPM
	OR	AL,AL
	JZ	L_34	
	JMP	WRTERR
L_34:
CLOZE:	MOV	DX,(Offset OUTFCB)	;CLOSE THE OUTPUT FILE
	MOV	CL,CLOSE
	CALL	CPM
;BALANCE FOPT
; Directory for	all user areas completed.  If the multi-disk option
; is enabled and selected, reset to the	base user area and repeat
; the directory	for next drive on-line until we	either exceed the
; drives in our	LODRV-HIDRV table, or the BDOS shuts us	down with
; a select or bad sector error,	which will be intercepted back to
; the EXIT module.
NXTDSK:	MOV	BX,(Offset FNDFLG)	;Get file found flag
	MOV	AL,M
	MOV	M,0			;Clear file found flag for next drive
	OR	AL,AL
	JNZ	NDSK			;Continue if at least 1 file found
;If file output enabled, disable temporarily
	MOV	BX,(Offset FOPFLG)
	DEC	M
	PUSH	BX
	MOV	AL,Byte Ptr .FCB	;Stash ASCII directory drive in NO FILE msg
	ADD	AL,'A'-1
	MOV	Byte Ptr NOFMS2,AL
	MOV	DX,(Offset NOFMS1)	;Print "No file on ? - "
	CALL	PRINT
	CALL	PRTFRE			;Tag with free message
;Restore original file output mode
	POP	BX
	INC	M
NDSK:					;If multi-disk option enabled
	MOV	AL,Byte Ptr DOPFLG	;If multi-disk not selected - skip next
	OR	AL,AL
	JNZ	NPRT
	CALL	CKABRT			;Check for user	abort first
	MOV	AL,STSKIP		;Skip if STart SKIP
	MOV	BX,FCB			;Bump directory	FCB drive code
	INC	M
	CMP	AL,M
	JNZ	NEXT1
	MOV	AL,SKIPTO		;If STSKIP then SKIPTO
	MOV	M,AL
NEXT1:	MOV	AL,(Offset HIDRV)-(Offset LODRV)	;Get maximum drive code	to search
	CMP	AL,M			;Does next disk	exceed maximum?
	JNAE	L_35	
	JMP	NOOPT			;Search	next disk if not
L_35:
;Balance DOPT
NPRT:					;if no printer, fall through to EXIT
;now, check if printer is in use
	MOV	AL,Byte Ptr POPFLG
	OR	AL,AL			;printer active?
	JZ	L_36	
	JMP	EXIT			;no, just exit...
L_36:
	MOV	CL,5
	MOV	DL,13			;print a CRLF
	CALL	CPM
	MOV	DL,10			;line feed
	CALL	CPM
;POPT
	JMP	EXIT			;All done - exit to CCP
; Print "Directory for....
PRDIR:	MOV	DX,(Offset TOTMS1)	;Print [CR,LF]  " Drive	"
	CALL	PRINT
	MOV	AL,Byte Ptr .FCB
	ADD	AL,'A'-1
	CALL	TTYPE			;Output	the drive code
	MOV	DX,(Offset TOTMS2)	;Print ", USER "
	CALL	PRINT
	CALL	TYPUSR			;Output	the user number
	RET
; Print	the user number	of the directory in decimal
TYPUSR:
	MOV	AL,Byte Ptr NEWUSR
	CMP	AL,10			;If user no. > 9 print leading 1
	JB	DUX
	MOV	AL,'1'
	CALL	TTYPE
	MOV	AL,Byte Ptr NEWUSR	;Print low digit of user no.
	SUB	AL,10
DUX:	ADD	AL,'0'
	JMPS	TTYPE
; Force	new line on video and check for	page pause
CRLF:	MOV	AL,0DH			;Send CR
	CALL	TTYPE
	MOV	AL,0AH			;Send LF
	JMPS	TTYPE			;Exit to caller	from TTYPE
; Separate the directory output	on a line with a space,	the delimiter,
; followed by another space.
FENCE:	CALL	SPACE
	MOV	AL,Byte Ptr MDELIM	;Fence character
FPAD:	CALL	TTYPE			;Print it, fall	into space
SPACE:	MOV	AL,' '			;Fall through to TTYPE
; Output character in A to console, and optionally to printer and/or
; the output file.
TTYPE:	PUSH	CX
	PUSH	DX
	PUSH	BX
	PUSH	AX			;Save the character to output
	CALL	TYPE2			;Send it to console
	POP	AX			;Restore the output character
	AND	AL,7FH			;Strip parity bit on character
; Test file output mode and skip to page pause test if not active.
	MOV	CH,AL			;Save stripped character to B
	MOV	AL,Byte Ptr FOPFLG	;Is file output	active?
	OR	AL,AL
	JNZ	NOWRIT			;Go check for page pause if not
; File output mode active - make sure we have room in buffer to add
; next character.  If buffer full, write out current record first
; and then start a new record with current character.
	MOV	BX,Word Ptr BUFPNT	;Get current buffer pointer
	MOV	AL,Byte Ptr BUFCNT	;Get buffer capacity remaining
	OR	AL,AL
	JNZ	PUTBUF			;Continue if buffer not full
	CALL	SETFOP			;Set the DMA address
	MOV	DX,(Offset OUTFCB)	;Otherwise, write the current buffer out
	MOV	CL,WRITE
	CALL	CPM			;(Note call must save character	in B)
	OR	AL,AL
	JZ	L_37	
	JMP	WRTERR			;Take write error exit if disk full or R/O
L_37:
	MOV	BX,(Offset OUTBUF)	;Reset buffer pointer
	MOV	AL,128			;Reset buffer capacity
PUTBUF:	MOV	M,CH			;Shove character to next buffer position
	INC	BX			;Bump buffer pointer
	MOV	Word Ptr BUFPNT,BX	;.. and save it
	DEC	AL			;Dock count of characters left in buffer
	MOV	Byte Ptr BUFCNT,AL	;..and save it
NOWRIT:	MOV	AL,CH			;Recall	stripped character
;Balance FOPT
;If printer option
	AND	AL,7FH			;Strip parity bit on character
	MOV	DL,AL			;Setup list output call
	MOV	CL,5
	MOV	AL,Byte Ptr POPFLG	;Test printer flag
	OR	AL,AL
	JNZ	L_38	
	CALL	CPM			;Print character if flag true
L_38:
	MOV	AL,DL			;Recall	character
	CMP	AL,0AH			;Do we have a LF?
	JNZ	TYPRET			;Exit if not
	MOV	AL,Byte Ptr NOPFLG	;Is the	page pause function disabled?
	OR	AL,AL
	JZ	TYPRET			;Exit if so
	MOV	AL,Byte Ptr LINCNT	;Get line count
	INC	AL			;Bump it
	MOV	BX,(Offset MLPS)
	CMP	AL,M			;Are we	at the end of the screen?
	JB	NOTEOS			;Skip if not
	MOV	DX,(Offset EOSMSG)	;Else, display pause message
	MOV	CL,9			;..without checking for	LFs
	INT	224
	CALL	CINPUT			;Wait for character
	CMP	AL,'C'-40H
	JNZ	L_39	
	JMP	EXIT			;Abort on CTRL-C
L_39:
	XOR	AL,AL			;Reset line count
NOTEOS:	MOV	Byte Ptr LINCNT,AL	;Save new line count
TYPRET:	POP	BX			;Exit from TTYPE
	POP	DX
	POP	CX
	RET
; Output character
TYPE2:
	IF	REVIDEO
	TEST	AL,80H			;is high bit set?
	JZ	TYPE99			;jmp if not
	PUSH	AX			;save char.
	MOV	AL,Byte Ptr MLEADIN	;Send ESC char first
	CALL	TYPE99
	MOV	AL,Byte Ptr MINTOREV	;INTO REV VIDEO
	CALL	TYPE99
	MOV	AL,Byte Ptr MINTOREV+1
	CALL	TYPE99
	POP	AX			;Retrieve character
	AND	AL,7FH
	CALL	TYPE99
	MOV	AL,Byte Ptr MLEADIN	;Send ESC first
	CALL	TYPE99
	MOV	AL,Byte Ptr MOUTAREV	;The out of reverse video character
	CALL	TYPE99
	MOV	AL,Byte Ptr MOUTAREV+1	;The out of reverse video character
TYPE99:
	ENDIF				;Balance REVIDEO
	MOV	DL,AL			;Get character into BDOS entry register
	MOV	CL,WRCHR
	INT	224			;Call CONOUT via the BDOS
	RET
;Balance DIRCON
; Print	a string at HL of length B
TYPEIT:	MOV	AL,M
	CALL	TTYPE
	LAHF
	INC	BX
	SAHF
	DEC	CH
	JNZ	TYPEIT
	RET
; Print	string terminated with binary 0 on console.
PRINT:	MOV	SI,DX
	MOV	AL,[SI]
	CMP	AL,0
	JNZ	L_40
	RET
L_40:
	CALL	TTYPE
	INC	DX
	JMPS	PRINT
; Fetch	character from console (without	echo)
CINPUT:	MOV	DL,0FFH
	MOV	CL,6
	INT	224
	CMP	AL,0
	JZ	CINPUT			;wait for char.
	AND	AL,7FH
	RET
; Check	for a CTRL-C or	CTRL-S entered from the	keyboard.  Jump	to
; exit if CTRL-C, pause	on CTRL-S.
CKABRT:	MOV	DL,0FEH
	MOV	CL,6			;Check status of keyboard
	INT 	224			;Any key pressed?
	OR	AL,AL
	JNZ	L_41	
	RET				;No, return to caller
L_41:
	CALL	CINPUT			;Get character
	CMP	AL,'C'-40H		;CTRL-C?
	JNZ	L_42	
	JMP	EXIT			;If CTRL-C then	quit
L_42:
	CMP	AL,'S'-40H		;CTRL-S?
	JZ	L_43	
	RET				;No, return to caller
L_43:
	CALL	CINPUT			;Yes, wait for another char.
	CMP	AL,'C'-40H		;Might be CTRL-C
	JNZ	L_44	
	JMP	EXIT			;Exit if CTRL-C, else fall thru	and continue
L_44:
	RET
; Kludge to allow call to address in HL
GOHL:	JMP	BX
; Entry	to BDOS	saving all extended registers
CPM:	PUSH	CX
	PUSH	DX
	PUSH	BX
	INT	224
	MOV	CH,AL			; Save return code
	MOV	AL,Byte Ptr VERFLG	; Is this 3.0 or higher?
	CMP	AL,30H
	MOV	AL,CH
	JB	CPM20			; No, exit normally
	CMP	AL,0FFH			; It is 3.0 - was return code FF?
	JNZ	CPM20			; No, exit normally
	MOV	AL,BH			; 3.0 and A=FF - check for extended error code
	OR	AL,AL
	JZ	L_45	
	JMP	DSKERR			; Trap out if we got a physical error
L_45:
	MOV	AL,CH			; Else continue normally
CPM20:
	POP	BX
	POP	DX
	POP	CX
	RET
; For file output mode,	return to old user area	and set	DMA for
; the file output buffer.
SETFOP:
	MOV	AL,Byte Ptr OLDUSR	;Get user number at startup
	MOV	DL,AL
	MOV	CL,CURUSR
	CALL	CPM			;Reset the old user number

	MOV	DX,(Offset OUTBUF)	;Move DMA from search buffer into the
	JMPS	SET2			;..output buffer
	RET
; Move disk buffer DMA to default buffer for directory search operations
; and BDOS media change routines (necessary for pre-CP/M 2 systems while
; in file output mode with an active buffer).
SETSRC:	MOV	DX,BASE+80H
SET2:	MOV	CL,SETDMA
	JMPS	CPM
; Print	the amount of free space remaining on the selected drive
PRTFRE:	MOV	BX,Word Ptr FREEBY	;Get space left before adding to MASTER.DIR
	CALL	DECPRT			;Print K free
	MOV	DX,(Offset TOTMS6)	;Print " FREE."
	JMP	PRINT
; Compare routine for sort
COMPR:	PUSH	BX			;Save table addr
	MOV	DL,M			;Load low order
	INC	BX
	MOV	DH,M			;Load high order
	INC	BX
	MOV	CL,M
	INC	BX
	MOV	CH,M
; BC, DE now point to entries to be compared
	XCHG	BX,DX
	MOV	DL,AL			;Get count
CMPLP:	MOV	AL,M
	AND	AL,7FH
	MOV	DH,AL
	MOV	SI,CX
	MOV	AL,[SI]
	AND	AL,7FH
	CMP	AL,DH
	LAHF
	INC	BX
	SAHF
	LAHF
	INC	CX
	SAHF
	JNZ	NOTEQL			;Quit on mismatch
	DEC	DL			;Or end	of count
	JNZ	CMPLP
NOTEQL:	POP	BX
	RET				;Cond code tells all
; Swap entries in the order table
SWAP:	MOV	CX,(Offset ORDER)-2	;Table base
	SHL	BX,1			;*2
	LAHF				;+ base
	ADD	BX,CX
	SAHF
	XCHG	BX,DX
	SHL	BX,1			;*2
	LAHF				;+ base
	ADD	BX,CX
	RCR	SI,1
	SAHF
	RCL	SI,1
	MOV	CL,M
	MOV	SI,DX
	MOV	AL,[SI]
	XCHG	BX,DX
	MOV	M,CL
	MOV	SI,DX
	MOV	[SI],AL
	LAHF
	INC	BX
	SAHF
	LAHF
	INC	DX
	SAHF
	MOV	CL,M
	MOV	SI,DX
	MOV	AL,[SI]
	XCHG	BX,DX
	MOV	M,CL
	MOV	SI,DX
	MOV	[SI],AL
	RET
; New compare routine - modified for filetype sort
COMPARE:
	MOV	CX,(Offset ORDER)-2
	SHL	BX,1
	ADD	BX,CX
	XCHG	BX,DX
	SHL	BX,1
	ADD	BX,CX
	XCHG	BX,DX
	MOV	CL,M
	INC	BX
	MOV	CH,M
	XCHG	BX,DX
	MOV	DL,M
	INC	BX
	MOV	DH,M
	XCHG	BX,DX
;If T option true
	MOV	AL,Byte Ptr TOPFLG	;Get option flag
	OR	AL,AL			;test for zero
	JNZ	CMPFNFT
;
; Compare by Filename, Filetype, and Extent - in that order
;
	PUSH	BX
	PUSH	CX
	MOV	DX,8			;Pt to FT
	CALL	ADDEM
	MOV	DL,3			; 3 bytes in type
	CALL	CMPLPE			;compare FT'S
	POP	CX
	POP	BX
	JZ	L_47	
	RET				;Continue if match
L_47:
	PUSH	BX
	PUSH	CX
	MOV	DL,8			;8 bytes in name
	CALL	CMPLPE			;Compare FN'S
	POP	CX
	POP	BX
	JZ	L_48	
	RET				;Continue if match
L_48:
	MOV	DX,11			;Pt to EXT
	CALL	ADDEM
	MOV	SI,CX			;Compare EXT'S
	MOV	AL,[SI]
	CMP	AL,M
	RET
ADDEM:	PUSH	BX			;Adds DE to HL & BC
	MOV	BH,CH
	MOV	BL,CL
	LAHF
	ADD	BX,DX
	SAHF
	MOV	CH,BH
	MOV	CL,BL
	POP	BX
	LAHF
	ADD	BX,DX
	RCR	SI,1
	SAHF
	RCL	SI,1
	RET
;
;  Compare by Filename, Filetype, Extent - in that order
;
CMPFNFT:
	MOV	DL,12			;Compare FN, FT, EX
; and fall into routine
CMPLPE:
	MOV	AL,M
	AND	AL,7FH
	MOV	DH,AL
	MOV	SI,CX
	MOV	AL,[SI]
	AND	AL,7FH
	CMP	AL,DH
	LAHF
	INC	CX
	SAHF
	LAHF
	INC	BX
	SAHF
	JZ	L_49
	RET
L_49:
	DEC	DL
	JNZ	CMPLPE
	RET
HELP:	INC	BX			;Make sure there is a 
	CMP	AL,M			;second "/" else return
	LAHF				;since some people use / 
	DEC	BX
	SAHF
	JZ	L_50	
	RET				;for a file name
L_50:
HELP1:	MOV	DX,(Offset HLPMES)	;Print a short HELP message
	CALL	PRINT
	CALL	CRLF
	JMPS	EXIT
; Error	exit
ERXIT:
	MOV	AL,-1
	MOV	Byte Ptr FOPFLG,AL	;Disable file output mode on error
	CALL	CRLF			;Space down
	POP	DX			;Get pointer to	message	string
	CALL	PRINT			;Print it
	MOV	DX,(Offset ERRMS1)	;Print " Error"
	CALL	PRINT
	CALL	CRLF			;Space down
; Exit - all done, restore stack
EXIT:	MOV	CL,CONST		;Check console status
	CALL	CPM
	OR	AL,AL			;Char waiting?
	MOV	CL,RDCHR
	JZ	L_51	
	CALL	CPM			;Gobble	up char
L_51:
;Restore BDOS intercept	vectors
	MOV	AL,Byte Ptr VERFLG	;Or error mode, depending on version
	CMP	AL,30H
	JB	EXIT0
	MOV	CL,45
	MOV	DL,0			;Set error mode back to default
	CALL	CPM
	JMPS	EXIT1
EXIT0:
	MOV	AL,Byte Ptr DOPFLG	;..if they were swapped
	OR	AL,AL
	JNZ	L_52	
	CALL	SWAPEM
L_52:
EXIT1:
	MOV	AL,Byte Ptr ROPFLG	;If disk system	was reset
	OR	AL,AL
	MOV	AL,Byte Ptr OLDDSK	;Get default disk at startup
	MOV	DL,AL
	MOV	CL,SELDSK		;Reselect it
	JNZ	L_53	
	CALL	CPM
L_53:
;Balance ROPT
	MOV	CL,0			;EXIT PROGRAM
	MOV	DL,0
	INT	224


; Trap BDOS select and sector error vectors to our own intercept
; routine so we	can catch a reference to an illegal drive.
SWAPEM:	MOV	AL,Byte Ptr VERFLG	;Check version
	CMP	AL,30H			;see if error mode call is available
	JB	SWAP20			;If not, use BDOS error vectors
	MOV	CL,45
	MOV	DL,0FFH			;Use Set Error Mode call
	CALL	CPM			;set "return code only" mode
SWAP20:	RET				;this doesn't make sense in CP/M-86
					;...version 1.1
; Check	CP/M version number.  Return carry flag	set if pre-CP/M	2.
; If CP/M 2 or later or	MP/M (any version), return carry clear.
CKVER:	MOV	AL,Byte Ptr VERFLG
	CMP	AL,20H
	RET
; Recovery point from intercepted BDOS select and bad sector errors.
DSKERR:
	JMP	EXIT			;..and exit back to CCP
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
;	SUBROUTINES to read library file directory
;
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
PRTLMEM:MOV	BX,(Offset ORDER)	;Initialize order table	pointer
	MOV	Word Ptr NEXTL,BX
	XOR	AL,AL
	MOV	Byte Ptr LNCNT,AL
ENTRYL:	MOV	BX,Word Ptr LCOUNT
	MOV	AL,BH			;Is this the last file?
	OR	AL,BL
	JZ	LBEXIT			;If COUNT=0, last file so RETURN
	LAHF				;Dock file count
	DEC	BX
	SAHF
	MOV	Word Ptr LCOUNT,BX
	PUSH	CX
	CALL	CKABRT			;Check for abort code from keyboard
	MOV	BX,Word Ptr NEXTL
	MOV	AL,11
	CALL	COMPR			;Does this entry match next one?
	POP	CX
	JNZ	LBRTST			;No, print it
	INC	BX
	INC	BX			;Skip since highest extent comes last in list
	MOV	Word Ptr NEXTL,BX
	JMPS	ENTRYL			;Loop back for next lowest extent
;
;	Exit Library member printing
;
LBEXIT:	MOV	BX,Word Ptr LMTOTL
	MOV	AL,BH
	OR	AL,BL
	JNZ	L_54
	RET
L_54:
	PUSH	BX			;Save Member count
	XOR	AL,AL			;Get a zero to...
	MOV	Byte Ptr SUPSPC,AL	;Suppress leading spaces in totals
	MOV	BX,(Offset MNPL)
	MOV	AL,Byte Ptr LNCNT
	CMP	AL,M
	JZ	L_55	
	CALL	CRLF
L_55:
	MOV	DX,(Offset CONTM)	;Print "There are "
	CALL	PRINT
	POP	BX			;GET TOTAL MEMBER COUNT BACK
	CALL	DECPRT
	MOV	DX,(Offset MFILES)	;PRINT "Members in "
	CALL	PRINT
	MOV	BX,Word Ptr LBTOTL
	CALL	DECPRT
	MOV	DX,(Offset LIBR)
	CALL	PRINT
	RET
; Valid	entry obtained - spit it out.
LBRTST:	MOV	BX,Word Ptr NEXTL	;Get order table pointer
	MOV	DL,M			;Get low order address
	LAHF
	INC	BX
	SAHF
	MOV	DH,M			;Get high order	address
	LAHF
	INC	BX
	SAHF
	MOV	Word Ptr NEXTL,BX	;Save updated table pointer
	MOV	BX,8
	LAHF
	ADD	BX,DX
	SAHF
	CALL	CKLBR
	JZ	L_56	
	JMP	ENTRYL
L_56:
	PUSH	DX
	MOV	BX,(Offset MNPL)
	MOV	AL,Byte Ptr LNCNT
	CMP	AL,M
	JZ	L_57	
	CALL	CRLF
L_57:
	MOV	BX,(Offset LFMSEP)
	MOV	CH,31
	CALL	TYPEIT
	POP	BX
	PUSH	BX
	MOV	CH,8			;File name length
	CALL	TYPEIT
	MOV	AL,'.'			;Period	after FN
	CALL	TTYPE
	MOV	CH,3			;Display 3 characters of filetype
	CALL	TYPEIT
	CALL	CRLF
	POP	BX
;
; Saves the library file name into LBRFCB
;
	MOV	AL,Byte Ptr .FCB
	MOV	DX,(Offset LBRFCB)	;TO
	MOV	SI,DX
	MOV	[SI],AL
	LAHF
	INC	DX
	SAHF
	MOV	CH,11			;LEN
	CALL	MOVE			;DO THE MOVE
	XCHG	BX,DX
	MOV	CH,25
CLMFCB:	MOV	M,0
	LAHF
	INC	BX
	SAHF
	DEC	CH
	JNZ	CLMFCB
	CALL	SETLDMA
	MOV	DX,(Offset LBRFCB)	;POINT TO FILE
	MOV	CL,OPEN			;GET FUNCTION
	CALL	CPM			;OPEN IT
	MOV	CL,READ
	MOV	DX,(Offset LBRFCB)
	CALL	CPM
	CALL	SETFOP
	MOV	BX,(Offset LBBUF)
	MOV	AL,M
	OR	AL,AL
	JZ	CKLDIR			;check directory present?
BADLBR:	MOV	BX,(Offset NLBRF)
	MOV	CH,25
	CALL	TYPEIT
LMLEXI:	CALL	LBCLOSE
	JMP	ENTRYL
L_58	EQU	$
	DSEG
	ORG	Offset L_58
NLBRF	DB	'++Not a LIBRARY file.++',13,10
L_59	EQU	$
	CSEG
	ORG	Offset L_59
;
;	Close the library file
;
LBCLOSE:MOV	DX,(Offset LBRFCB)
	MOV	CL,CLOSE
	CALL	CPM
	RET
;
; SETLDMA - Set the Library file DMA adderss
;
SETLDMA:
	MOV	AL,Byte Ptr NEWUSR	;Get user area for directory
	MOV	DL,AL
	MOV	CL,CURUSR		;Get the user function
	CALL	CPM			;..and set new user number

	MOV	DX,(Offset LBBUF)
	MOV	CL,SETDMA
	CALL	CPM
	RET
;
; CKLDIR - check to see if there indeed is a LBR file
;	  directory and barf if not
;
CKLDIR:	MOV	CH,11			;len of file name
	MOV	AL,' '			;space
	INC	BX
CKDLP:	CMP	AL,M
	JNZ	BADLBR
	DEC	CH
	LAHF
	INC	BX
	SAHF
	JNZ	CKDLP
;
; The first entry in the LBR directory is indeed blank.
; Now see if the directory size is >0
;
	MOV	DL,M			;file starting location low
	INC	BX			;must be zero here
	MOV	AL,M			;file starting location high
	OR	AL,DL			;must be zero here also
	JNZ	BADLBR
	INC	BX
	MOV	DL,M			;get library size low
	INC	BX			;point to library size high
	MOV	DH,M			;get library size high
	MOV	AL,DH
	OR	AL,DL			;library must have some size
	JNZ	L_61	
	JMP	BADLBR
L_61:
	DEC	DX
	XCHG	BX,DX
	MOV	Word Ptr SLFILE,BX
	MOV	BX,Word Ptr LBTOTL
	INC	BX
	MOV	Word Ptr LBTOTL,BX
	MOV	AL,Byte Ptr MNPL
	MOV	Byte Ptr LNCNT,AL	;Reset names per line counter
	MOV	CH,3
	MOV	BX,17
	ADD	BX,DX
	MOV	AL,Byte Ptr MNPL
	MOV	Byte Ptr LNCNT,AL	;Reset names per line counter
	JMPS	LMTEST
LFMLOP:	MOV	BX,Word Ptr SLFILE	;GET
	MOV	AL,BL
	OR	AL,BH
	JNZ	L_62	
	JMP	LMLEXI
L_62:
	LAHF
	DEC	BX
	SAHF
	MOV	Word Ptr SLFILE,BX
	CALL	SETLDMA
	MOV	CL,READ
	MOV	DX,(Offset LBRFCB)
	CALL	CPM
	CALL	SETFOP
	MOV	CH,4			;GET FILE COUNT PER SECTOR
	MOV	BX,(Offset LBBUF)	;GET BUFFER STARTING ADDRESS
LMTEST:	MOV	AL,M			;Get member open flag
	OR	AL,AL			;TEST FOR OPEN
	JZ	PRMNAM
LMTESA:	MOV	DX,32			;Member not open get offset
	ADD	BX,DX			;to next and add it in.
	DEC	CH			;is buffer empty ?
	JNZ	LMTEST			;No so test next entry
	JMPS	LFMLOP			;Yes get next buffer...
PRMNAM:	PUSH	BX			;Print member NAME and SIZE
	PUSH	CX
	CALL	CKABRT			;Check for abort code from keyboard
	MOV	BX,(Offset LNCNT)
	MOV	AL,Byte Ptr MNPL
	CMP	AL,M
	JNZ	PRMNA1
	MOV	AL,3			;If printing less than 4 wide
	CMP	AL,M
	JB	PRMNA1
	MOV	AL,Byte Ptr LBRFCB	;.. precede new	line with drive	name
	ADD	AL,'A'-1
	CALL	TTYPE
;If reporting user numbers
	CALL	TYPUSR

	MOV	AL,':'			;Tag header with a colon and a space
	CALL	FPAD			;..and exit back to ENTRY
PRMNA1:	POP	CX
	POP	BX
	PUSH	BX
	PUSH	CX
	LAHF
	INC	BX
	SAHF
	MOV	CH,8			;File name length
	CALL	TYPEIT
	MOV	AL,'.'			;Period	after FN
	CALL	TTYPE
	MOV	CH,3			;Display 3 characters of filetype
	CALL	TYPEIT
	LAHF
	INC	BX
	SAHF
	LAHF
	INC	BX
	SAHF
	MOV	DL,M
	LAHF
	INC	BX
	SAHF
	MOV	DH,M
	XCHG	BX,DX
; If report size enabled, output the size of the individual file.
;If file size report wanted
	CALL	DECPRT			;..go print it
	MOV	AL,'s'			;..and follow with S size
	CALL	TTYPE
; At least one more file to output - can we put	it on the current line?
	MOV	BX,Word Ptr LMTOTL
	LAHF
	INC	BX
	SAHF
	MOV	Word Ptr LMTOTL,BX
	MOV	AL,Byte Ptr LNCNT
	DEC	AL
	MOV	Byte Ptr LNCNT,AL
	LAHF
	XCHG	AL,AH
	PUSH	AX
	JZ	L_64	
	CALL	FENCE			;If room left, output the fence	character
L_64:
	POP	AX
	XCHG	AL,AH
	SAHF
	POP	CX
	POP	BX
	JZ	L_65	
	JMP	LMTESA			;.. and	go output another file
L_65:
; Current line full, start a new one.
	MOV	AL,Byte Ptr MNPL
	MOV	Byte Ptr LNCNT,AL	;Reset names per line counter
	CALL	CRLF			;Space down to next line
	JMP	LMTESA
;
; Move characters from 'HL' to 'DE' length in 'B'
;
MOVE:	MOV	AL,M			;GET A CHAR
	MOV	SI,DX			;STORE IT
	MOV	[SI],AL
	LAHF				;TO NEXT "FROM"
	INC	BX
	SAHF
	LAHF				;TO NEXT "TO"
	INC	DX
	SAHF
	DEC	CH			;MORE?
	JNZ	MOVE			;   YES, LOOP
	RET				;   NO, RETURN
;
; cklbr -- test file extent for LBR
;
CKLBR:	PUSH	BX
	PUSH	DX
	PUSH	CX
	XCHG	BX,DX
	MOV	BX,(Offset LBRTYP)
	MOV	CL,3
CKLBL:	MOV	SI,DX
	MOV	AL,[SI]
	AND	AL,7FH
	CMP	AL,M
	JNZ	CKLBX
	LAHF
	INC	BX
	SAHF
	LAHF
	INC	DX
	SAHF
	DEC	CL
	JNZ	CKLBL
CKLBX:	POP	CX
	POP	DX
	POP	BX
	RET
L_66	EQU	$
	DSEG
	ORG	Offset L_66
;
LFMSEP	DB	'    Library file members for : '
LBRTYP	DB	'LBR'
L_67	EQU	$
	CSEG
	ORG	Offset L_67
;LOPT
;**********************
;		      *
; End of Program Code *
;		      *
;**********************
; Initialized data area
DREMSG	DB	'Driv','e',0
EOSMSG	DB	'    [More]',0DH,'$'
ERRMS1	DB	' '
ERRMS2	DB	'Erro','r',0
ERRTAG	DB	' -','>',0
NOFMS1	DB	0DH,0AH,'No file on '
NOFMS2	DB	'  -',' ',0
DIRMS1	DB	0DH,0AH,'  Directory fo','r',0
TOTMS1	DB	'  Drive',' ',0
TOTMS2	DB	', user',' ',0
TOTMS3	DB	' contains',' ',0
TOTMS4	DB	'k in',' ',0
TOTMS5	DB	' files with',' ',0
TOTMS6	DB	'k free','.',0DH,0AH,0	;,0DH,0AH ,0
USRMSG	DB	'User ','#',0
CONTM	DB	'    There are',' ',0
MFILES	DB	' Member Files in',' ',0
LIBR	DB	' Library(s)','.',0
FNDFLG	DB	0			;Flag whether any files matched
LINCNT	DB	0			;Count of lines	printed	on screen
; Drive	code/user area lookup table
; Note that the	LODRV-HIDRV table is included here fully configured.
; For your own use, you	should change the maximum user areas as
; appropriate for each drive on	your system, and then delete any
; DBs referencing drives that don't exist.  Note also that there
; are only 16 user areas available under CP/M 2, so the highest
; legal user area you can specify is 15 (range 0-15 = 16 areas).
; The program will convert anything over 15 into mod 15.
; NOTE: If some drives in active table are unused then skip these
; drives by using STSKIP and SKIPTO equates.
LODRV	EQU	(Offset $)		;Mark beginning	of drive/user table
	DB	15			;Maximum user area for Drive A
	DB	15			;   "	  "    "    "	 "   B
	DB	00			;   "	  "    "    "	 "   C
	DB	00			;   "	  "    "    "	 "   D
	DB	00			;   "	  "    "    "	 "   E
	DB	00			;   "	  "    "    "	 "   F
	DB	00			;   "	  "    "    "	 "   G
	DB	00			;   "	  "    "    "	 "   H
	DB	00			;   "	  "    "    "	 "   I
	DB	00			;   "	  "    "    "	 "   J
	DB	00			;   "	  "    "    "	 "   K
	DB	00			;   "	  "    "    "	 "   L
	DB	15			;   "	  "    "    "	 "   M
;	DB	15		;   "	  "    "    "	 "   N
;	DB	15		;   "	  "    "    "	 "   O
;	DB	15		;   "	  "    "    "	 "   P
HIDRV	EQU	(Offset $)		;Mark end of drive/user	table
; Option field lookup table.
; Note that you	can force any of these options as a DEFAULT by
; changing the letter for the option into a zero (assuming that
; its enabling equate is true).	 Each option that you hard-wire	in
; this manner will no longer be	recognized as a	command	line OPTION,
; and if you redundantly key it in, SD will flag it as unrecognized.
OTBL	EQU	(Offset $)		;Mark start of option table
;All users-option flag
AOPFLG	DB	'A'
;Multi-disk-option flag
DOPFLG	DB	'D'
;File-output-option flag
FOPFLG	DB	'F'
;Display Library members flag
LOPFLG	DB	'L'
;No page-pause option flag
NOPFLG	DB	'N'
;Printer option	flag
POPFLG	DB	'P'
;Reset option flag
ROPFLG	DB	'R'
;System	file option flag
SOPFLG	DB	'S'
TOPFLG	DB	'T'			;Type sort option flag
;Display version number flag
VOPFLG	DB	'V'
OEND	EQU	(Offset $)		;Mark end of option table
; End of option	lookup table
;	IF	AOPT
;PASS1: DB	0		;First pass flag for all user file output
;	ENDIF
VECTBL	DW	(Offset DSKERR)		;BDOS SECTOR ERROR INTERCEPT VECTOR
	DW	(Offset DSKERR)		;BDOS SELECT ERROR INTERCEPT VECTOR
BUFPNT	DW	(Offset OUTBUF)		;POINTER TO NEXT LOCATION IN OUTPUT BUFFER
BUFCNT	DB	128			;NUMBER OF BYTES LEFT IN OUTPUT BUFFER
opnflg	DB	0			;file open flag for all user file output
OUTFCB	DB	0,'SD      DIR'
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
OUTBUF	RS	128			;OUTPUT FILE BUFFER
GOTUSR	DB	0
; Uninitialized	data area
BASUSR	RS	1			;Dupe of original directory user # to search
BLKMAX	RS	2			;Highest block # on drive
BLKMSK	RS	1			;SEC/BLK - 1
BLKSHF	RS	1			;# shifts to mult by SEC/BLK
COUNT	RS	2			;Entry count
DIRMAX	RS	2			;Highest file #	in directory
FREEBY	RS	2			;Contains number of K left on directory	drive
GAP	RS	2			;Sort routine storage
I	RS	2			; ditto
J	RS	2			; ditto
JIG	RS	2			; ditto
LZFLG	RS	1			;Zero when printing leading zeros
MAXUSR	RS	1			;Maximum user #	for drive from lookup table
NEWUSR	RS	1			;Contains user number selected by "$U" option
NEXTT	RS	2			;Next table entry
OLDDSK	RS	1			;Holder	for currently logged-in	drive
OLDUSR	RS	1			;Contains user number upon invocation
SCOUNT	RS	2			;# to sort
SUPSPC	RS	1			;Leading space flag for	decimal	routine
TBLOC	RS	2			;Pointer to start of name table
TEMP	RS	2			;Save dir entry
TOTFIL	RS	2			;Total number of files
TOTSIZ	RS	2			;Total size of all files
VERFLG	RS	1			;CP/M version number (0=pre-CP/M 2)
LMTOTL	DW	0
LBTOTL	DW	0
LNCNT	DB	0
LCOUNT	DW	0
NEXTL	DW	0
SLFILE	DW	0
LBRFCB	RS	36
LBBUF	RS	80H
ORDER	EQU	(Offset $)		;Order table starts here
	END
